package jmetal.metaheuristics.singleObjective.island;

import java.util.Comparator;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import jmetal.core.Algorithm;
import jmetal.core.Problem;
import jmetal.core.Solution;
import jmetal.core.SolutionSet;
import jmetal.util.JMException;
import jmetal.util.comparators.ObjectiveComparator;

public class IslandLTGAController extends Algorithm {
	private static final int NUM_ISLANDS = 5;
	
	public static int evaluationsTotal;

	/**
	 * 
	 * Constructor Create a new GGA instance.
	 * 
	 * @param problem
	 *            Problem to solve.
	 */
	public IslandLTGAController(Problem problem) {
		super(problem);
		evaluationsTotal = 0;
	} // GGA

	/**
	 * Execute the GGA algorithm
	 * 
	 * @throws JMException
	 */
	public SolutionSet execute() throws JMException, ClassNotFoundException {
		List<IslandLTGA> islands = new Vector<>();

		for (int i = 0; i < NUM_ISLANDS; i++) {
			islands.add(new IslandLTGA(this.problem_));
		}
		int populationSize;
		int maxEvaluations;
		int evaluations;

		SolutionSet population;
		SolutionSet offspringPopulation;

		Comparator comparator;
		comparator = new ObjectiveComparator(0); // Single objective comparator
		// Read the params
		populationSize = ((Integer) this.getInputParameter("populationSize")).intValue();
		maxEvaluations = ((Integer) this.getInputParameter("maxEvaluations")).intValue();
		int islandPopulationSize = populationSize / NUM_ISLANDS;
		int maxEpocas = 100;
		int maxEvaluationsIsland = (maxEvaluations / maxEpocas) / NUM_ISLANDS;

		// Initialize the variables
		population = new SolutionSet(populationSize);
		offspringPopulation = new SolutionSet(populationSize);

		evaluations = 0;

		// Create the initial population
		Solution newIndividual;
		for (int i = 0; i < populationSize; i++) {
			newIndividual = new Solution(problem_);
			problem_.evaluate(newIndividual);
			evaluationsTotal++;
			population.add(newIndividual);
		} // for

		SolutionSet imigrantes = new SolutionSet(islandPopulationSize);
		SolutionSet imigrantesFilhos = new SolutionSet(islandPopulationSize);

		// Sort population
		population.sort(comparator);
		ExecutorService executor = Executors.newFixedThreadPool(NUM_ISLANDS);
		while (evaluations < maxEpocas) {
			imigrantes.clear();
			for (int i = 0; i < islandPopulationSize; i++) {
				imigrantes.add(population.get(i));
			}
			for (IslandLTGA island : islands) {
				configureIsland(islandPopulationSize, maxEvaluationsIsland, island);
				imigrantesFilhos = island.execute(imigrantes, executor);
				for (int i = 0; i < imigrantesFilhos.size(); i++) {
					offspringPopulation.add(imigrantesFilhos.get(i));
				}
			}
			islands.clear();
			for (int i = 0; i < NUM_ISLANDS; i++) {
				islands.add(new IslandLTGA(this.problem_));
			}

			// The offspring population becomes the new current population
			population.clear();
			for (int i = 0; i < populationSize; i++) {
				population.add(offspringPopulation.get(i));
			}
			evaluations++;
			offspringPopulation.clear();
			population.sort(comparator);
		} // while

		// Return a population with the best individual
		SolutionSet resultPopulation = new SolutionSet(1);
		resultPopulation.add(population.get(0));
		executor.shutdown();
		// System.out.println("Evaluations: " + evaluations);
		return resultPopulation;
	} // execute

	private void configureIsland(int islandPopulationSize, int maxEvaluationsIsland, IslandLTGA island) {
		island.addOperator("crossover", this.getOperator("crossover"));
		island.addOperator("mutation", this.getOperator("mutation"));
		island.addOperator("selection", this.getOperator("selection"));
		island.setInputParameter("populationSize", islandPopulationSize);
		island.setInputParameter("maxEvaluations", maxEvaluationsIsland);
	}
} // gGA